let index = 0

/**
 * event-emitter class like nodejs events module
 *
 * @class EventEmitter
 */
class EventEmitter {
  constructor () {
    this._listeners = {}
  }

  /**
   * Subscribe an event
   *
   * @param {string} evt The event you want to subscribe
   * @param {function} listener The event handler
   * @returns {string} an id that you can use it to off the event handler
   * @memberof EventEmitter
   */
  on (evt, listener) {
    const id = ++index
    this._listeners[evt] = this._listeners[evt] || []
    this._listeners[evt].push({
      id,
      listener,
      once: false
    })
    return id
  }

  /**
   * Subscribe an event once
   *
   * @param {string} evt The event you want to subscribe
   * @param {function} listener The event handler
   * @returns {string} an id that you can use it to off the event handler
   * @memberof EventEmitter
   */
  once (evt, listener) {
    const id = ++index
    this._listeners[evt] = this._listeners[evt] || []
    this._listeners[evt].push({
      id,
      listener,
      once: true
    })
    return id
  }

  /**
   * Unsubscribe an event
   *
   * @param {string} evt The event you want to unsubscribe
   * @param {function} listener  The event listener or id which is returned by `on` or `once`
   * @memberof EventEmitter
   */
  off (evt, listener) {
    this._listeners[evt] = this._listeners[evt] || []
    for (let i = 0; i < this._listeners[evt].length; i++) {
      const handler = this._listeners[evt][i]
      if (handler.id === listener || handler.listener === listener) {
        this._listeners[evt].splice(i, 1)
        break
      }
    }
  }

  /**
   * Trigger an event
   *
   * @param {string} evt The event you want to trigger
   * @param {arguments} args  Event args
   * @memberof EventEmitter
   */
  trigger (evt, ...args) {
    this._listeners[evt] = this._listeners[evt] || []
    let newListener = [...this._listeners[evt]]
    for (let i = this._listeners[evt].length - 1; i >= 0; i--) {
      const handler = this._listeners[evt][i]
      handler.listener(...args)
      if (handler.once) {
        newListener.splice(i, 1)
      }
    }
    this._listeners[evt] = newListener
  }
}

export {EventEmitter}
